Load packages
library(tidyverse)
library(labelled)
library(kableExtra)
library(cmdstanr)
library(posterior)
library(bayestestR)
library(bayesplot)
library(matrixStats)
library(plotly)
library(lubridate)
theme_set(theme_minimal(base_size = 12))Death from any cause or requirement of new intensive respiratory support (invasive or non-invasive ventilation) or vasopressor/inotropic support in the 28 days after randomisation.
Undertake the analysis of the primary outcome for ASCOT.
library(tidyverse)
library(labelled)
library(kableExtra)
library(cmdstanr)
library(posterior)
library(bayestestR)
library(bayesplot)
library(matrixStats)
library(plotly)
library(lubridate)
theme_set(theme_minimal(base_size = 12))source("r/derive_full_datasets.r")
all_dat <- read_all_no_daily()The primary outcome is a composite if death, need for new respiratory support, or vasopressor/inotropic support. From the protocol:
Death from any cause or requirement of new intensive respiratory support (invasive or non-invasive ventilation) or vasopressor/inotropic support in the 28 days after randomisation. This includes any participant who receives non-invasive mechanical ventilation (either CPAP or BIPAP, apart from the below considerations) any time after enrolment even if not transferred to ICU. It does NOT include the use of humidified high-flow nasal prong oxygen.
Participants on pre-existing home BiPAP or CPAP will not be considered to have met the primary outcome unless they have either:
- required invasive mechanical ventilation (i.e. intubation), or
- graduated from CPAP only whilst asleep to BiPAP at any time, or
- graduated from BiPAP only whilst asleep to BiPAP for >12 hours/day, or
- died by day 28
There may be cases where a patient has been assessed as requiring intensive respiratory support (invasive or non-invasive ventilation) or vasopressor/inotropic support, but the patient or family declined treatment and the patient was discharged home. If attempts to obtain 28-day data are unsuccessful or not possible, and the investigator had deemed at the time of discharge that the patient would be highly likely to die within 28 days from randomisation, these participants will be deemed to have met the primary outcome.
Derivation of the outcome requires checking of the daily, discharge, and day 28 data extracts. On the daily data, there is a variable DD_PrimaryEndpointReachedToday however this was coded incorrectly in the original database and therefore fails to capture some participants. Additionally, given the composite nature of the outcome it is useful to check all components individually as well as the composite outcome. Therefore, this variable is not used in the derivation, but is included as a cross-check.
Below, each component is summarised in aggregate.
There were 50 participants who had died by day 28, 40 were are reported as a discharge status of death and 10 were reported as a death post-discharge. Day 28 mortality was missing or unknown for 30 participants.
all_dat %>%
filter(ENR_rec == 1, WTH_FU == 0) %>%
select(DIS_death, DIS_day, D28_death) %>%
dplyr::count(DIS_death, DIS_day <= 28, D28_death) %>%
spread(D28_death, n, fill = 0)# A tibble: 4 × 5
DIS_death `DIS_day <= 28` `0` `1` `<NA>`
<int> <lgl> <dbl> <dbl> <dbl>
1 0 FALSE 10 0 0
2 0 TRUE 1489 10 30
3 1 FALSE 3 0 0
4 1 TRUE 0 40 0
Vasopressor/inotropic support is reported on both the daily (from day 1 to discharge) and the day 28 (from discharge to day 28) extracts. There were 39 participants who required vasopressor/inotropic support. Of these, 19 were reported between discharge and day 28, 18 were reported prior to discharge, and 2 were reported for both. The component was missing for 35 participants. Either due to missing information at day 28 (33) or due to missing daily information (2).
all_dat %>%
filter(ENR_rec == 1, WTH_FU == 0) %>%
select(DAILY_missing, ANY_vasop, ANY_DD_vasop, D28_vasop) %>%
dplyr::count(DAILY_missing, ANY_DD_vasop, D28_vasop, ANY_vasop) %>%
spread(ANY_vasop, n, fill = 0)# A tibble: 7 × 6
DAILY_missing ANY_DD_vasop D28_vasop `0` `1` `<NA>`
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 0 0 0 1508 0 0
2 0 0 1 0 19 0
3 0 0 NA 0 0 33
4 0 1 0 0 18 0
5 0 1 1 0 2 0
6 1 NA 0 0 0 1
7 1 NA NA 0 0 1
Respiratory support is reported on both the daily (from day 1 to discharge) and the day 28 (at day 28) extracts. There were 70 participants who required new intensive respiratory support. Of these, 60 were prior to discharge, 6 were reported at day 28, and 4 were both. The outcome was unknown for 36 participants due to either missing day 28 information (34) or missing daily information (2).
all_dat %>%
filter(ENR_rec == 1, WTH_FU == 0) %>%
select(DAILY_missing, ANY_vent, ANY_DD_vent, D28_vent) %>%
dplyr::count(DAILY_missing, ANY_DD_vent, D28_vent, ANY_vent) %>%
spread(ANY_vent, n, fill = 0)# A tibble: 6 × 6
DAILY_missing ANY_DD_vent D28_vent `0` `1` `<NA>`
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 0 0 0 1476 0 0
2 0 0 NA 0 0 34
3 0 1 0 0 60 0
4 0 1 1 0 4 0
5 0 1 NA 0 6 0
6 1 NA 0 0 0 2
There were 3 participants who were DAMA and identified as likely to die. However, the day 28 status was observed for all 3 participants, therefore, no-one met the primary endpoint due to DAMA and unknown day 28 status.
all_dat %>%
filter(ENR_rec == 1, WTH_FU == 0) %>%
select(DIS_DAMA, DIS_DAMAlikelytodie, D28_death) %>%
dplyr::count(DIS_DAMA, DIS_DAMAlikelytodie, D28_death) %>%
spread(D28_death, n)# A tibble: 3 × 5
DIS_DAMA DIS_DAMAlikelytodie `0` `1` `<NA>`
<int> <int> <int> <int> <int>
1 0 0 1469 46 28
2 1 0 32 2 2
3 1 1 1 2 NA
There were 107 (7% of observed outcomes) participants who met the composite outcome and 36 (2% of all outcomes) participants whose outcome was unknown. Of these, 107 who met the outcome, 12 met all three criteria (death, vasopressor, ventilation) and the remaining met some subset of them. Of the 36 with missing outcome, 26 were missing all three components.
all_dat %>%
filter(ENR_rec == 1, WTH_FU == 0) %>%
dplyr::count(PO)# A tibble: 3 × 2
PO n
<dbl> <int>
1 0 1439
2 1 107
3 NA 36
all_dat %>%
filter(ENR_rec == 1, WTH_FU == 0) %>%
select(PO, D28_death, ANY_vasop, ANY_vent) %>%
dplyr::count(D28_death, ANY_vasop, ANY_vent, PO) %>%
spread(PO, n, fill = 0)# A tibble: 15 × 6
D28_death ANY_vasop ANY_vent `0` `1` `<NA>`
<int> <dbl> <dbl> <dbl> <dbl> <dbl>
1 0 0 0 1439 0 0
2 0 0 1 0 31 0
3 0 1 0 0 20 0
4 0 1 1 0 5 0
5 0 NA 0 0 0 5
6 0 NA NA 0 0 2
7 1 0 0 0 10 0
8 1 0 1 0 21 0
9 1 0 NA 0 4 0
10 1 1 0 0 2 0
11 1 1 1 0 12 0
12 1 NA NA 0 1 0
13 NA 0 NA 0 0 3
14 NA NA 1 0 1 0
15 NA NA NA 0 0 26
agedat <- all_dat %>%
filter(ENR_rec == 1, WTH_FU == 0) %>%
dplyr::count(PO, AgeAtEntry) %>%
spread(PO, n, fill = 0) %>%
mutate(p = `1` / (`1` + `0`))
agemod <- glm(
cbind(`1`, `0`) ~ AgeAtEntry,
data = agedat,
family = binomial())
agedat <- agedat %>%
mutate(
ypred = predict(agemod, newdata = agedat, type = "response")
)
p <- ggplot(agedat, aes(AgeAtEntry, p)) +
geom_point() +
geom_line(aes(y = ypred)) +
labs(y = "Proportion with outcome", x = "Age at entry")
ggplotly(p)all_dat %>%
filter(ENR_rec == 1, WTH_FU == 0) %>%
dplyr::count(`Age 60+` = agegte60, PO) %>%
group_by(`Age 60+`) %>%
spread(PO, n, fill = 0) %>%
mutate(
p_1 = `1` / (`1` + `0`),
p_miss = `<NA>` / (`1` + `0` + `<NA>`)
) %>%
kable(digits = 2) %>%
kable_styling(bootstrap_options = "striped")| Age 60+ | 0 | 1 | <NA> | p_1 | p_miss |
|---|---|---|---|---|---|
| 0 | 1038 | 59 | 25 | 0.05 | 0.02 |
| 1 | 401 | 48 | 11 | 0.11 | 0.02 |
all_dat %>%
filter(ENR_rec == 1, WTH_FU == 0) %>%
dplyr::count(Country = PT_CountryName, PO) %>%
group_by(Country) %>%
spread(PO, n, fill = 0) %>%
mutate(
p_1 = `1` / (`1` + `0`),
p_miss = `<NA>` / (`1` + `0` + `<NA>`)
) %>%
kable(digits = 2) %>%
kable_styling(bootstrap_options = "striped")| Country | 0 | 1 | <NA> | p_1 | p_miss |
|---|---|---|---|---|---|
| Australia | 128 | 8 | 14 | 0.06 | 0.09 |
| India | 1190 | 83 | 15 | 0.07 | 0.01 |
| Nepal | 104 | 11 | 3 | 0.10 | 0.03 |
| New Zealand | 17 | 5 | 4 | 0.23 | 0.15 |
tabdat <- all_dat %>%
filter(ENR_rec == 1, WTH_FU == 0) %>%
dplyr::count(
Country = PT_CountryName,
Site = PT_LocationName,
PO) %>%
group_by(Country, Site) %>%
spread(PO, n, fill = 0) %>%
mutate(
p_1 = `1` / (`1` + `0`),
p_miss = `<NA>` / (`1` + `0` + `<NA>`)
) %>%
ungroup()
n_country <- tabdat %>%
dplyr::count(Country) %>%
pull(n)
cn <- cumsum(n_country)
tabdat %>%
select(-Country) %>%
kable(digits = 2) %>%
kable_styling(bootstrap_options = "striped") %>%
pack_rows("Australia", 1, cn[1]) %>%
pack_rows("India", cn[1] + 1, cn[2]) %>%
pack_rows("Nepal", cn[2] + 1, cn[3]) %>%
pack_rows("New Zealand", cn[3] + 1, cn[4])| Site | 0 | 1 | <NA> | p_1 | p_miss |
|---|---|---|---|---|---|
| Australia | |||||
| Alfred Hospital | 6 | 1 | 7 | 0.14 | 0.50 |
| Blacktown Hospital | 4 | 0 | 0 | 0.00 | 0.00 |
| Box Hill Hospital | 4 | 2 | 2 | 0.33 | 0.25 |
| Campbelltown Hospital | 11 | 1 | 0 | 0.08 | 0.00 |
| John Hunter Hospital | 2 | 0 | 0 | 0.00 | 0.00 |
| Liverpool Hospital | 6 | 2 | 0 | 0.25 | 0.00 |
| Monash Health | 8 | 0 | 0 | 0.00 | 0.00 |
| Prince Charles Hospital | 3 | 0 | 0 | 0.00 | 0.00 |
| Royal Melbourne Hospital | 7 | 1 | 1 | 0.12 | 0.11 |
| Royal North Shore Hospital | 6 | 0 | 0 | 0.00 | 0.00 |
| Royal Perth Hospital | 5 | 0 | 1 | 0.00 | 0.17 |
| St George Hospital | 1 | 0 | 0 | 0.00 | 0.00 |
| St Vincent's Hospital, Sydney | 10 | 0 | 1 | 0.00 | 0.09 |
| Wagga Wagga Base Hospital | 10 | 0 | 0 | 0.00 | 0.00 |
| Western Health | 1 | 0 | 0 | 0.00 | 0.00 |
| Westmead Hospital | 42 | 1 | 2 | 0.02 | 0.04 |
| Wollongong Hospital | 2 | 0 | 0 | 0.00 | 0.00 |
| India | |||||
| Aditya Multispeciality Hospital | 63 | 0 | 0 | 0.00 | 0.00 |
| Apex Hospitals, Malviya Nagar | 125 | 1 | 0 | 0.01 | 0.00 |
| Believers Church Medical College Hospital | 18 | 0 | 0 | 0.00 | 0.00 |
| Christian Medical College Vellore | 84 | 16 | 10 | 0.16 | 0.09 |
| CMC Ludhiana | 64 | 18 | 0 | 0.22 | 0.00 |
| Core Hospital | 86 | 21 | 0 | 0.20 | 0.00 |
| Jivanrekha Multispeciality Hospital | 329 | 13 | 3 | 0.04 | 0.01 |
| Maharaja Agrasen Superspeciality Hospital | 138 | 0 | 0 | 0.00 | 0.00 |
| Samishta Hospital & Research Institute | 156 | 2 | 0 | 0.01 | 0.00 |
| Sterling Multispecialty Hospital Pune | 85 | 12 | 2 | 0.12 | 0.02 |
| Symbiosis University Hospital | 42 | 0 | 0 | 0.00 | 0.00 |
| Nepal | |||||
| Bir Hospital | 71 | 8 | 3 | 0.10 | 0.04 |
| Tribhuvan University Teaching Hospital | 33 | 3 | 0 | 0.08 | 0.00 |
| New Zealand | |||||
| Christchurch Hospital | 1 | 0 | 0 | 0.00 | 0.00 |
| Dunedin Hospital | 1 | 0 | 1 | 0.00 | 0.50 |
| Middlemore Hospital | 13 | 5 | 0 | 0.28 | 0.00 |
| North Shore Hospital | 2 | 0 | 3 | 0.00 | 0.60 |
caldat <- all_dat %>%
filter(ENR_rec == 1, WTH_FU == 0) %>%
dplyr::count(PO, yr = year(RandDate), mth = month(RandDate)) %>%
spread(PO, n, fill = 0) %>%
mutate(p = `1` / (`1` + `0`))
p <- ggplot(caldat, aes(mth, p)) +
facet_wrap( ~ yr) +
geom_point() +
labs(y = "Proportion with outcome", x = "Calendar date") +
scale_x_continuous(breaks = 1:12)
ggplotly(p)all_dat %>%
filter(ENR_rec == 1, WTH_FU == 0) %>%
dplyr::count(`Anti-coagulation` = CAssignment, PO) %>%
group_by(`Anti-coagulation`) %>%
spread(PO, n, fill = 0) %>%
mutate(
p_1 = `1` / (`1` + `0`),
p_miss = `<NA>` / (`1` + `0` + `<NA>`)
) %>%
kable(digits = 2) %>%
kable_styling(bootstrap_options = "striped")| Anti-coagulation | 0 | 1 | <NA> | p_1 | p_miss |
|---|---|---|---|---|---|
| C0 | 16 | 4 | 6 | 0.20 | 0.23 |
| C1 | 551 | 45 | 14 | 0.08 | 0.02 |
| C2 | 571 | 30 | 12 | 0.05 | 0.02 |
| C3 | 258 | 21 | 4 | 0.08 | 0.01 |
| C4 | 43 | 7 | 0 | 0.14 | 0.00 |
all_dat %>%
filter(ENR_rec == 1, WTH_FU == 0) %>%
dplyr::count(
Country = PT_CountryName,
`Anti-coagulation` = CAssignment,
PO) %>%
complete(Country, `Anti-coagulation`, PO, fill = list(n = 0)) %>%
group_by(Country, `Anti-coagulation`) %>%
spread(PO, n, fill = 0) %>%
ungroup() %>%
mutate(
p_1 = `1` / (`1` + `0`),
p_miss = `<NA>` / (`1` + `0` + `<NA>`)
) %>%
select(-Country) %>%
kable(digits = 2) %>%
kable_styling(bootstrap_options = "striped") %>%
pack_rows("Australia", 1, 5) %>%
pack_rows("India", 6, 10) %>%
pack_rows("Nepal", 11, 15) %>%
pack_rows("New Zealand", 16, 20)| Anti-coagulation | 0 | 1 | <NA> | p_1 | p_miss |
|---|---|---|---|---|---|
| Australia | |||||
| C0 | 16 | 2 | 6 | 0.11 | 0.25 |
| C1 | 45 | 2 | 2 | 0.04 | 0.04 |
| C2 | 50 | 3 | 6 | 0.06 | 0.10 |
| C3 | 7 | 0 | 0 | 0.00 | 0.00 |
| C4 | 10 | 1 | 0 | 0.09 | 0.00 |
| India | |||||
| C0 | 0 | 0 | 0 | NaN | NaN |
| C1 | 449 | 37 | 7 | 0.08 | 0.01 |
| C2 | 486 | 25 | 5 | 0.05 | 0.01 |
| C3 | 251 | 21 | 3 | 0.08 | 0.01 |
| C4 | 4 | 0 | 0 | 0.00 | 0.00 |
| Nepal | |||||
| C0 | 0 | 0 | 0 | NaN | NaN |
| C1 | 49 | 4 | 3 | 0.08 | 0.05 |
| C2 | 30 | 1 | 0 | 0.03 | 0.00 |
| C3 | 0 | 0 | 0 | NaN | NaN |
| C4 | 25 | 6 | 0 | 0.19 | 0.00 |
| New Zealand | |||||
| C0 | 0 | 2 | 0 | 1.00 | 0.00 |
| C1 | 8 | 2 | 2 | 0.20 | 0.17 |
| C2 | 5 | 1 | 1 | 0.17 | 0.14 |
| C3 | 0 | 0 | 1 | NaN | 1.00 |
| C4 | 4 | 0 | 0 | 0.00 | 0.00 |